/*
 Copyright (C) 2007 - Michael Opitz
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "gmail-ui.h"
#include "gmail.h"
#include "gmail-list.h"

#include <glade/glade.h>
#include <string.h>

static GObjectClass *parent_class = 0;

/* 
 * Actually, we must pass a GmailList to the GmailUI class.
 */
enum {
	PROP_LIST = 1
};

static GType type = 0;

#define LOG(m) printf ("%s\n", (m))

#define GMAIL_GLADE_FILE 	\
	"/.gnome2/epiphany/extensions/data/gmailmanager/gmail.glade"

#define GMAIL_UI_GET_PRIVATE(object) 	(G_TYPE_INSTANCE_GET_PRIVATE (\
					 (object), \
					 GMAIL_UI_TYPE, \
					 GmailUIPriv))
/** 
 * This struct contains all the private data for the GmailUI class
 */
struct _GmailUIPriv 
{
	/** 
	 * This attribute stores an reference to the GmailList, which stores 
	 * all the available GmailAccounts.
	 */
	GmailList *list;

	/** 
	 * This attribute stores the add-button with which you can add a new
	 * account.
	 */
	GtkButton *add_button;
	/** 
	 * This attribute stores the remove-buttons with which you can remove an
	 * account.
	 */
	GtkButton *rem_button;
	/** 
	 * This attribute stores the close-button which destroys the dialog.
	 */
	GtkButton *close_button;
	/** 
	 * This attribute stores the combo-box with which you can select an
	 * account.
	 */
	GtkComboBox *combo_box;
	/** 
	 * This attribute stores the GtkDialog in which all the other widgets
	 * are stored.
	 */
	GtkDialog *dialog;

	/** 
	 * This GtkEntry displays the e-mail address of the selected
	 * gmail-account.
	 */
	GtkEntry *mail_entry;
	/** 
	 * This GtkEntry displays the password of the selected gmail-account.
	 */
	GtkEntry *password_entry;

	/** 
	 * This attribute indicates whether the GmailUI class is disposed or
	 * not.
	 */
	gboolean is_disposed;
	/** 
	 * This attribute represents a reference to a signal-handler.
	 */
	guint sig_handler;
	/** 
	 * This attribute stores the gmail-address of the active account.
	 */
	char *active_tmp;
};

/*
 * This are the usual GObject init/destroy functions...
 */
static void
gmail_ui_class_init (GmailUIClass *klass);
void
gmail_ui_class_finalize (gpointer g_class, gpointer class_data);
void 
gmail_ui_instance_finalize (GObject *obj);
void 
gmail_ui_instance_init (GTypeInstance *instance, gpointer g_class);
void 
gmail_ui_instance_dispose (GObject *object);

/** 
 * gmail_ui_show_dialog:
 * 
 * @self: is a GmailUI object.
 *
 * This function displays the GmailUI-dialog.
 * 
 */
void 
gmail_ui_show_dialog (GmailUI *self);
/** 
 * gmail_ui_dlg_add:
 * 
 * @ptr: is a GtkButton object.
 * @user_data: is a pointer to a GmailUI object.
 * 
 * This callback is called when the add new account button was pressed.
 * It creates a new dialog in which you can enter your new account data.
 * 
 */
void 
gmail_ui_dlg_add (GtkButton *ptr, gpointer user_data);
/** 
 * gmail_ui_dlg_rem:
 * 
 * @ptr: is a pointer to a GtkButton object.
 * @user_data: is a pointer to a GmailUI object.
 * 
 * This callback is called when the remove account button was pressed. It
 * deletes the currently selected account.
 *
 */
void 
gmail_ui_dlg_rem (GtkButton *ptr, gpointer user_data);
/** 
 * gmail_ui_dlg_close:
 * 
 * @ptr: is a pointer to a GtkButton object.
 * @user_data: is a pointer to a GmailUI object.
 *
 * This callback is called when the close-button in the GmailUI-dialog was
 * pressed. It destroys the GmailUI dialog.
 *
 */
void 
gmail_ui_dlg_close (GtkButton *ptr, gpointer user_data);
/** 
 * gmail_ui_dlg_close_dialog:
 * 
 * @dia: is a pointer to a GtkDialog
 * @response_id: is a gint which indicates the response-id
 * @user_data: is a pointer to a GmailUI object.
 *
 * This callback is called when the account-creation-dialog was closed. It
 * deletes the account-creation-dialog.
 *
 */
void 
gmail_ui_dlg_close_dialog (GtkDialog *dia, gint response_id, gpointer user_data);
/** 
 * gmail_ui_changed_cb:
 * 
 * @list: is a pointer to a GmailList object.
 * @self: is a pointer to a GmailUI object.
 * 
 * This callback is called when the GmailList was changed. It synchronizes the
 * combobox with the new GmailList.
 *
 */
void
gmail_ui_changed_cb (GmailList *list, GmailUI *self);
/** 
 * gmail_ui_changed_set_model:
 * 
 * @model: a pointer to a GtkTreeModel
 * @path: a pointer to a GtkTreePath
 * @iter: a pointer to a GtkTreeIter
 * @self: a pointer to the GmailUI object.
 *
 * This function is called after gmail_ui_changed_cb and sets the previously
 * active account to the active account again after the box was synchronized.
 * 
 * Returns: always FALSE
 */
gboolean 
gmail_ui_changed_set_model (GtkTreeModel *model, GtkTreePath *path, 
			    GtkTreeIter *iter, GmailUI *self);
/** 
 * gmail_ui_sync_box:
 * 
 * @ui: is a pointer to the GmailUI object.
 * @box: is a pointer to a GtkComboBox
 *
 * This function syncs the GmailUI combobox with a GmailList.
 *
 */
void 
gmail_ui_sync_box (GmailUI *ui, GtkComboBox *box);
/** 
 * gmail_ui_sync_box_insert_cb:
 * 
 * @acc: a pointer to a GmailAccount
 * @box: a pointer to a GtkComboBox
 * 
 * This function is called by the gmail_ui_sync_box function and inserts an
 * entry for the GmailAccount into the combobox.
 *
 */
void 
gmail_ui_sync_box_insert_cb (GmailAccount *acc, GtkComboBox *box);
/** 
 * gmail_ui_new_user_ok_cb:
 * 
 * @button: is a pointer to a GtkButton
 * @user_data: is a pointer to a GtkDialog
 *
 * This function is called by the new account dialog when the OK button was
 * pressed. It destroys the dialog and returns the corresponding response-id.
 * 
 * Returns: FALSE
 */
gboolean 
gmail_ui_new_user_ok_cb (GtkButton *button, gpointer user_data);
/** 
 * gmail_ui_new_user_cancel_cb:
 * 
 * @button: a pointer to a GtkButton
 * @user_data: a pointer to a GtkDialog
 * 
 * This function is called by the new account dialog when the Cancel button was
 * pressed. It destroys the dialog and returns the corresponding response-id.
 *
 * Returns: FALSE
 */
gboolean 
gmail_ui_new_user_cancel_cb (GtkButton *button, gpointer user_data);
/** 
 * gmail_ui_dlg_edited:
 * 
 * @widget: is a pointer to a GtkWidget
 * @user_data: is a pointer to a GmailUI
 *
 * This function is called when either the mail-textbox or the password-textbox
 * was edited. It modifies the GmailList model and emitts a gmail-list-changed
 * signal.
 *
 */
void
gmail_ui_dlg_edited (GtkWidget *widget, gpointer user_data);
/** 
 * gmail_ui_dlg_changed:
 * 
 * @box: is a pointer to a GtkComboBox
 * @user_data: is a pointer to a GmailUI object.
 *
 * This function is called when a new combobox entry was selected. It changes
 * the entries in the mail-textfield and the password textfield to the contents
 * of the selected GmailAccount.
 *
 */
void
gmail_ui_dlg_changed (GtkComboBox *box, gpointer user_data);

/** 
 * gmail_ui_init_gtk:
 * 
 * @ui: is a pointer to a GmailUI object.
 *
 * This function loads the glade-file and connects all the signals.
 *
 */
void
gmail_ui_init_gtk (GmailUI *ui);

static void
gmail_ui_set_property (GObject *obj,
		       guint prop_id,
		       const GValue *value,
		       GParamSpec *pspec)
{
	GmailUI *ui = GMAIL_UI (obj);
	switch (prop_id) {
	case PROP_LIST:
		if (ui->priv->list != NULL) {
			g_signal_handler_disconnect (ui->priv->list, 
						     ui->priv->sig_handler);
			g_object_unref (ui->priv->list);
		}
		ui->priv->list = g_value_get_pointer (value);
		g_object_ref (ui->priv->list);
		ui->priv->sig_handler = g_signal_connect (ui->priv->list,
							  "gmail-list-changed", 
							  G_CALLBACK(
							  gmail_ui_changed_cb), 
							  ui);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
		break;
	}
}

static void
gmail_ui_get_property (GObject *obj,
		       guint prop_id,
		       GValue *value,
		       GParamSpec *pspec)
{
	GmailUI *ui = GMAIL_UI (obj);
	switch (prop_id) {
	case PROP_LIST:
		g_value_set_pointer (value, (gpointer)ui->priv->list);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
		break;
	}
}

gboolean 
gmail_ui_changed_set_model (GtkTreeModel *model, GtkTreePath *path, 
			    GtkTreeIter *iter, GmailUI *self)
{
	GValue tmp_val = {0, };
	memset (&tmp_val, 0, sizeof(GValue));
	gtk_tree_model_get_value (model, iter, 0, &tmp_val);
	const char *tmp = g_value_get_string (&tmp_val);
	if (!strcmp (tmp, self->priv->active_tmp)) {
		gtk_combo_box_set_active_iter (self->priv->combo_box, iter);
	}
	return FALSE;
}

void
gmail_ui_changed_cb (GmailList *list, GmailUI *self)
{
	char *active = gtk_combo_box_get_active_text (self->priv->combo_box);
	self->priv->active_tmp = active;
	gmail_ui_sync_box (self, self->priv->combo_box);
	/* check if active exists... */
	if (gmail_list_find_account (self->priv->list, active) == NULL)
		return;
	/* select active again... */
	GtkTreeModel *model = gtk_combo_box_get_model (self->priv->combo_box);
	gtk_tree_model_foreach (model, 
			        (GtkTreeModelForeachFunc) 
			        gmail_ui_changed_set_model, 
			        self);

	g_free (active);
	self->priv->active_tmp = NULL;
}

gboolean 
gmail_ui_new_user_ok_cb (GtkButton *button, gpointer user_data)
{
	GtkDialog *dlg = GTK_DIALOG (user_data);
	gtk_dialog_response (dlg, GTK_RESPONSE_ACCEPT);
	return FALSE;
}

gboolean 
gmail_ui_new_user_cancel_cb (GtkButton *button, gpointer user_data)
{
	GtkDialog *dlg = GTK_DIALOG (user_data);
	gtk_dialog_response (dlg, GTK_RESPONSE_REJECT);
	gtk_widget_hide (GTK_WIDGET(dlg)); 
	return FALSE;
}

/*
 * inserts the GList-Contents into the GtkComboBox...
 */
void 
gmail_ui_sync_box_insert_cb (GmailAccount *acc, GtkComboBox *box)
{
	g_return_if_fail (acc != NULL && box != NULL);
	char *mail;
	g_object_get (acc, "mail", &mail, NULL);
	gtk_combo_box_append_text (box, mail);
	g_free (mail);
}

/*
 * Syncs the combo box with the GList of Gmail-Accounts
 */
void 
gmail_ui_sync_box (GmailUI *ui, GtkComboBox *box)
{
	GtkListStore *store = GTK_LIST_STORE (gtk_combo_box_get_model(box));
	gtk_list_store_clear (store);

	g_assert (ui->priv != NULL);
	if (ui->priv->list == NULL) return;
	gmail_list_foreach (ui->priv->list, (GFunc) gmail_ui_sync_box_insert_cb,
			    box);
	gtk_combo_box_set_active (box, 0);
}

void
gmail_ui_dlg_edited (GtkWidget *widget, gpointer user_data)
{
	GmailUI *self = (GmailUI*) user_data;
	g_assert (self != NULL);
	g_return_if_fail (self->priv->combo_box != NULL);

	GtkTreeIter it;
	gtk_combo_box_get_active_iter (self->priv->combo_box, &it);
	char *txt = gtk_combo_box_get_active_text (self->priv->combo_box);
	if (self->priv->list == NULL || txt == NULL) return;
	GmailAccount *acc = gmail_list_find_account (self->priv->list, txt);
	g_return_if_fail (acc != NULL);

	g_object_set (G_OBJECT(acc), "mail", 
		      gtk_entry_get_text (self->priv->mail_entry),
		      "password", 
		      gtk_entry_get_text (self->priv->password_entry),
		      NULL);
	/*
	 * unfortunately we must update the combobox here, or else we can't
	 * restore the active combobox element if an updated occures :(
	 */
	GtkListStore *store = (GtkListStore*) gtk_combo_box_get_model(
			       self->priv->combo_box);
	g_return_if_fail (store != NULL);
	gtk_list_store_set (store, &it, 0, 
			    gtk_entry_get_text( self->priv->mail_entry), -1);

	gmail_list_emit_changed (self->priv->list);
}

void
gmail_ui_dlg_changed (GtkComboBox *box, gpointer user_data)
{
	GmailUI *self = (GmailUI*) user_data;
	g_return_if_fail (box != NULL);
	if (self->priv->list == NULL) return;

	char *mail = gtk_combo_box_get_active_text (box);
	g_return_if_fail (mail != NULL);
	GmailAccount *acc = gmail_list_find_account (self->priv->list, mail);
	g_return_if_fail (acc != NULL);

	char *val1, *val2;
	g_object_get (acc, "mail", &val1, "password", &val2, NULL);
	gtk_entry_set_text (self->priv->mail_entry, val1);
	gtk_entry_set_text (self->priv->password_entry, val2);
	g_free (val1);
	g_free (val2);
}

void gmail_ui_dlg_add (GtkButton *ptr, gpointer user_data)
{
	GmailUI *self = (GmailUI*) user_data;
	g_assert (self != NULL);

	char *glade_path = g_strconcat (g_get_home_dir (), GMAIL_GLADE_FILE,
				       NULL);
	GladeXML *xml = glade_xml_new (glade_path, NULL, NULL);
	g_free (glade_path);

	GtkDialog *dlg = GTK_DIALOG (glade_xml_get_widget (xml, 
				     "gmail_new_dlg"));
	GtkEntry *mail = GTK_ENTRY (glade_xml_get_widget (xml, "mail_entry"));
	GtkEntry *pass = GTK_ENTRY (glade_xml_get_widget (xml, 
				    "password_entry"));
	GtkButton *ok = GTK_BUTTON (glade_xml_get_widget (xml, "button_ok"));
	GtkButton *cancel = GTK_BUTTON (glade_xml_get_widget (xml,
					"button_cancel"));

	g_signal_connect (G_OBJECT (ok), "clicked",
			G_CALLBACK (gmail_ui_new_user_ok_cb), (gpointer) dlg);
	g_signal_connect (G_OBJECT (cancel), "clicked",
			 G_CALLBACK (gmail_ui_new_user_cancel_cb), 
			 (gpointer) dlg);

	int result = gtk_dialog_run (dlg);
	if (result != GTK_RESPONSE_ACCEPT) {
		return;
	}
	/* Create a new account */
	GmailAccount *acc = g_object_new (GMAIL_ACCOUNT_TYPE, NULL);
	g_object_set (G_OBJECT (acc), "mail", gtk_entry_get_text (mail), 
				    "password", gtk_entry_get_text (pass), NULL);
	if (self->priv->list == NULL) return;
	gmail_list_append_account (self->priv->list, acc);

	gtk_widget_hide (GTK_WIDGET (dlg));
}

void 
gmail_ui_dlg_rem (GtkButton *ptr, gpointer user_data)
{
	GmailUI *self = (GmailUI*) user_data;
	g_assert (self != NULL);

	GtkTreeIter iter;
	gtk_combo_box_get_active_iter (self->priv->combo_box, &iter);
	char *txt = gtk_combo_box_get_active_text (self->priv->combo_box);
	if (txt == NULL || self->priv->list == NULL) return;

	gmail_list_remove_account_by_mail (self->priv->list, txt);
}

void 
gmail_ui_dlg_close (GtkButton *ptr, gpointer user_data)
{
	GmailUI *ui = (GmailUI*) user_data;
	gmail_ui_dlg_edited (NULL, ui);
	gtk_widget_hide (GTK_WIDGET (ui->priv->dialog));
}

void 
gmail_ui_dlg_close_dialog (GtkDialog *dia, gint response_id, gpointer user_data)
{
	GmailUI *ui = (GmailUI*) user_data;
	if (response_id == GTK_RESPONSE_DELETE_EVENT) {
		gtk_widget_hide (GTK_WIDGET (ui->priv->dialog));
		gmail_ui_init_gtk (ui);
	}
}

GType
gmail_ui_register_type (GTypeModule *module)
{
	const GTypeInfo info = {
		sizeof (GmailUIClass),
		NULL, 		/* base_init */
		NULL,		/* base_finalize */
		(GClassInitFunc) gmail_ui_class_init, 
		NULL, 		/* class_finalize */
		NULL, 		/* class_data */
		sizeof (GmailUI), 
		0, 		/* n_preallocs */
		(GInstanceInitFunc) gmail_ui_instance_init 
	};
	type = g_type_module_register_type (module, G_TYPE_OBJECT, 
					    "GmailUI", &info, 0);
	return type;
}

GType 
gmail_ui_get_type ()
{
	return type;
}

void
gmail_ui_init_gtk (GmailUI *ui)
{
	char *path = g_strconcat (g_get_home_dir (), GMAIL_GLADE_FILE, NULL);
	GladeXML *xml = glade_xml_new (path, NULL, NULL);
	g_free(path);

	GtkDialog *dlg = GTK_DIALOG (glade_xml_get_widget (xml,
							   "gmail_edit_dlg"));
	GtkButton *add = GTK_BUTTON (glade_xml_get_widget (xml, "add_button"));
	GtkButton *rem = GTK_BUTTON (glade_xml_get_widget (xml, 
				     "remove_button"));
	GtkEntry *mail = GTK_ENTRY (glade_xml_get_widget (xml, 
				    "mail_entry_main"));
	GtkEntry *pass = GTK_ENTRY (glade_xml_get_widget (xml, 
				    "password_entry_main"));
	GtkButton *close = GTK_BUTTON (glade_xml_get_widget (xml, 
				       "close_button"));
	GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING);
	GtkComboBox *combo = GTK_COMBO_BOX (
			     glade_xml_get_widget (xml, "mail_box"));
	gtk_combo_box_set_model (combo, (GtkTreeModel*) store);
	GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
				        "text", 0, NULL);

	g_signal_connect_after (add, "clicked",
			        G_CALLBACK (gmail_ui_dlg_add),
			        ui);
	g_signal_connect_after (rem, "clicked",
			        G_CALLBACK (gmail_ui_dlg_rem),
			        ui);
	g_signal_connect_after (close, "clicked",
			        G_CALLBACK (gmail_ui_dlg_close),
			        ui);
	g_signal_connect_after (dlg, "response", 
			        G_CALLBACK (gmail_ui_dlg_close_dialog), 
			        ui);
	g_signal_connect_after (combo, "changed",
			        G_CALLBACK (gmail_ui_dlg_changed),
			        ui);
	g_signal_connect_after (mail, "activate", 
			        G_CALLBACK (gmail_ui_dlg_edited),
			        ui);
	g_signal_connect_after (pass, "activate", 
			        G_CALLBACK(gmail_ui_dlg_edited),
			        ui);

	ui->priv->add_button = add;
	ui->priv->rem_button = rem;
	ui->priv->close_button = close;
	ui->priv->combo_box = combo;
	ui->priv->dialog = dlg;
	ui->priv->mail_entry = mail;
	ui->priv->password_entry = pass;

	gmail_ui_sync_box (ui, combo);
}

static void
gmail_ui_class_init (GmailUIClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	parent_class = g_type_class_peek_parent (klass);

	object_class->finalize = gmail_ui_instance_finalize;
	object_class->dispose = gmail_ui_instance_dispose;
	object_class->set_property = gmail_ui_set_property;
	object_class->get_property = gmail_ui_get_property;
	klass->show_dialog = gmail_ui_show_dialog;

	g_type_class_add_private (object_class, 
				  sizeof (GmailUIPriv));

	GParamSpec *spec;
	spec = g_param_spec_pointer ("list", "Account List", 
				     "An GmailList instance",
				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
	g_object_class_install_property (object_class, PROP_LIST, spec);
}

void
gmail_ui_class_finalize (gpointer g_class, gpointer class_data)
{
}

void 
gmail_ui_instance_finalize (GObject *obj)
{
	G_OBJECT_CLASS (parent_class)->finalize(obj);
}

void 
gmail_ui_instance_init (GTypeInstance *instance, gpointer g_class)
{
	GmailUI *ui = GMAIL_UI (instance);
	ui->priv = GMAIL_UI_GET_PRIVATE (ui);
	ui->priv->list = NULL;
	ui->priv->is_disposed = FALSE;
}

void 
gmail_ui_instance_dispose (GObject *object)
{
	LOG ("GmailUI dispose");
	GmailUI *self = GMAIL_UI (object);
	self->priv->is_disposed = TRUE;
	if (self->priv->list) {
		g_signal_handler_disconnect (self->priv->list, 
					     self->priv->sig_handler);
		g_object_unref (self->priv->list);
	}
	if (self->priv->dialog) {
		gtk_widget_destroy (self->priv->dialog);
		self->priv->dialog = NULL;
	}
}

void 
gmail_ui_show_dialog (GmailUI *self)
{
	gmail_ui_init_gtk (self);
	gtk_dialog_run (self->priv->dialog);
}

